home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 22 code / PCI Driver Sample / NCR_DriverProject / Src / DriverPrepRequest.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-31  |  12.7 KB  |  319 lines  |  [TEXT/MPCC]

  1. /*                                    DriverPrepRequest.c                                */
  2. /*
  3.  * DriverPrepRequest.c
  4.  * Copyright © 1994-95 Apple Computer Inc. All rights reserved.
  5.  */
  6. /*    .___________________________________________________________________________________.
  7.       | These routines manage the details of calling PrepareMemoryForIO for a PBRead or    |
  8.       | or PBWrite request. The process is normally straightforward, but can become        |
  9.       | quite complex. The code flow for simple transfers is as follows:                    |
  10.       |        1. PBRead/PBWrite calls PrepareNewDMATransfer                                |
  11.       |        2. PrepareNewDMATransfer calls PrepareMemoryForIO and PrepareNextDMA        |
  12.       |        3. On return, the DMA parameters are sent to the hardware.                    |
  13.       | The more complex code paths handle the following problems:                        |
  14.       |    If the user area is discontiguous, PrepareNextDMA will be re-called from the    |
  15.       |        Primary Interrupt Service Routine to compute the next physical transfer        |
  16.       |        area and length. In many drivers this would be pre-processed into a            |
  17.       |        device-specific scatter-gather list.                                        |
  18.       |    If PrepareMemoryForIO did not prepare the entire area, it must be re-called        |
  19.       |        to handle partial preparation. In this case, the primary interrupt service    |
  20.       |        routine queues a Software Task and exits the interrupt without restarting    |
  21.       |        the device. When the Software Task runs, it calls PrepareMemoryForIO,        |
  22.       |        re-calls PrepareNextDMA, and queues a Secondary Interrupt handler that        |
  23.       |        restarts I/O. Error handling in this case is tricky, as an error must        |
  24.       |        cause the device to abort the transfer (otherwise subsequent transfers        |
  25.       |        won't be started.                                                            |
  26.       |    One further complication: if the hardware has a maximum transfer length, the    |
  27.       |    preparation might need to be re-started from the beginning (after updating        |
  28.       |    the callers buffer pointers and transfer length). This is handled by the        |
  29.       |    Software Task.                                                                    |
  30.       | Functions:                                                                        |
  31.       |    PrepareNewDMATransfer    Called from driver mainline to initialize variables and    |
  32.       |                            prepare the first DMA sequence.                            |
  33.       |    PrepareNextDMATask        Software Task queued from the primary interrupt to        |
  34.       |                            continue preparation.                                    |
  35.       |    PrepareNextDMA            Called after preparation and I/O to compute the next    |
  36.       |                            DMA physical address and transfer length.                |
  37.       |    ConfigureThisPhysicalArea Performs the actual address/length computation.        |
  38.       | PrepareNextDMA returns a status value that controls the caller:                    |
  39.       |    noErr                Success: start (or restart) I/O                                |
  40.       |    kPrepareMemoryRestart After a primary interrupt: the function queued a Software    |
  41.       |                        Task to handle partial preparation.    The interrupt routine    |
  42.       |                        should exit without restarting I/O.    When the Software Task    |
  43.       |                        completes, it will queue our Secondary Interrupt handler    |
  44.       |                        with this status to continue the I/O operation.                |
  45.       |    scsiDataRunError    Failure: The I/O table "done" state was set. This means the    |
  46.       |                        device wants to transmit more data than the application.    |
  47.       |                        The interrupt routine should halt the transfer.                |
  48.       |    other error            Failure from PrepareMemoryForIO. The interrupt routine        |
  49.       |                        should halt the transfer.                                    |
  50.       | The Software Task queues the common Secondary Interrupt Handler with status set    |
  51.       | as follows:                                                                        |
  52.       |    kPrepareMemoryRestart Success after partial preparation: restart I/O            |
  53.       |                        If there is an error, the SCSI Script will eventually        |
  54.       |                        fail through its I/O rundown and return scsiDataRunError    |
  55.       |                        (The only error returned by PrepareMemoryForIO is paramErr    |
  56.       |                        which isn't too useful.)                                    |
  57.     .___________________________________________________________________________________.
  58. */
  59. #include "NCRDriverPrivate.h"
  60. /*
  61.  * The current request will always be referenced through this local variable.
  62.  */
  63. #define REQUEST    (*perRequestDataPtr)
  64. #define PB        (*((IOParam *) REQUEST.pb))
  65. #define SCSI    (*((NCRSCSIParamPtr) PB.ioMisc))
  66. #define IOTABLE    (REQUEST.scsiIOTable)
  67.  
  68. OSErr                        DoInitialPreparation(
  69.         PerRequestDataPtr        perRequestDataPtr
  70.     );
  71.     
  72. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  73.  * PrepareNewDMATransfer initializes a user I/O data transfer request. It initializes
  74.  * the dma parameters, calls PrepareMemoryForIO, and specifies the first DMA address
  75.  * and length. If this transfer does not support a data phase, it returns noErr without
  76.  * preparing anything.
  77.  */
  78. OSErr
  79. PrepareNewDMATransfer(
  80.         PerRequestDataPtr        perRequestDataPtr
  81.     )
  82. {
  83.         OSErr                    status;
  84.         IOPreparationOptions    options;
  85.  
  86.         Trace(PrepareNewDMATransfer);
  87.         CLEAR(IOTABLE);
  88.         IOTABLE.preparationID = kInvalidID;        /* Marker for CheckpointIOTable        */
  89.         REQUEST.dmaFirstPrepared = 0;
  90.         REQUEST.dmaLengthPrepared = 0;
  91.         REQUEST.userBufferStart = NULL;
  92.         REQUEST.userBufferEnd = NULL;
  93.         switch (SCSI.driverAction) {
  94.         case kNCRDriverNoDataPhase:
  95.             status = noErr;
  96.             options = 0;
  97.             break;
  98.         case kNCRDriverInputAllowed:
  99.             options = kIOIsInput;
  100.             break;
  101.         case kNCRDriverOutputAllowed:
  102.             options = kIOIsOutput;
  103.             break;
  104.         default:
  105.             options = 0;
  106.             status = paramErr;
  107.             break;
  108.         }
  109.         if (options != 0) {                                /* Preparation required        */
  110.             REQUEST.userBufferStart = PB.ioBuffer;
  111.             REQUEST.amountToTransfer = PB.ioReqCount;
  112.             REQUEST.userBufferEnd = PB.ioBuffer + PB.ioReqCount;
  113.             PB.ioActCount = 0;
  114.             IOTABLE.options =
  115.                     ( options                            /* Input or output            */
  116.                     | (0 * kIOMultipleRanges)            /* No scatter-gather list    */
  117.                     | (1 * kIOLogicalRanges)            /* Logical addresses        */
  118.                     | (0 * kIOMinimalLogicalMapping)    /* Normal logical mapping    */
  119.                     | (1 * kIOShareMappingTables)        /* Share with Kernel        */
  120.                     | (0 * kIOCoherentDataPath)            /* No fancy data path        */
  121.                     );
  122.             IOTABLE.addressSpace = REQUEST.addressSpaceID;
  123.             IOTABLE.logicalMapping = NULL;
  124.             /*
  125.              * This is called from DoDriverIO or a Software Task to start a new
  126.              * I/O preparation. Note: we do not come here for partial preparation.
  127.              */
  128.             status = DoInitialPreparation(perRequestDataPtr);
  129.         }
  130.         return (status);
  131. }
  132.  
  133. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  134.  * PrepareNextDMATask is the SoftwareTask that is executed in order to start the next
  135.  * PrepareMemoryForIO request. Error handling needs to be improved.
  136.  */
  137. void
  138. PrepareNextDMATask(
  139.         void                    *p1,    /* perRequestDataPtr    */
  140.         void                    *p2        /* Unused                */
  141.     )
  142. {
  143.         OSErr                    status;
  144. #define perRequestDataPtr ((PerRequestDataPtr) p1)
  145.  
  146. LogString("\pPrepareNextDMATask - Software Task");
  147.         Trace(PrepareNextDMATask);
  148.         UNUSED(p2);
  149.         if ((IOTABLE.state & kIOStateDone) != 0) {
  150.             /*
  151.              * We need to start again (with the rest of the user buffer).
  152.              */
  153. LogString("\pPrepareNextDMATask: restart");
  154.             status = DoInitialPreparation(perRequestDataPtr);
  155.         }
  156.         else {
  157.             /*
  158.              * Partial preparation.
  159.              */
  160. LogDecimal(IOTABLE.firstPrepared, "\pfirstPrep: NextDMA calls PrepareMemoryForIO");
  161.             status = PrepareMemoryForIO(&REQUEST.scsiIOTable);
  162.             CheckStatus(status, "\pPrepareMemoryForIO partial prep");
  163.             if (status == noErr)
  164.                 status = PrepareNextDMA(perRequestDataPtr);
  165.         }
  166.         /*
  167.          * Restart I/O - we do this by calling our common Secondary Interrupt routine.
  168.          * The status value tells the Secondary Interrupt routine to restart the device..
  169.          */
  170. LogString("\pPrepareNextDMATask queues SIH");
  171.         (void) NCRQueueSecondaryInterrupt(perRequestDataPtr, kPrepareMemoryRestart);
  172. #undef perRequestDataPtr
  173. }
  174.  
  175.  
  176. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  177.  * DoInitialPreparation is called from the driver mainline to start a transfer, and
  178.  * from the Software Task to start a new preparation after updating the userBuffer
  179.  * pointers.
  180.  */
  181. OSErr
  182. DoInitialPreparation(
  183.         PerRequestDataPtr        perRequestDataPtr
  184.     )
  185. {
  186.         OSErr                    status;
  187.  
  188.         Trace(DoInitialPreparation);
  189.         if (REQUEST.userBufferStart >= REQUEST.userBufferEnd) {
  190.             /*
  191.              * The device has/needs more data than the caller supplied.
  192.              */
  193.             status = scsiDataRunError;
  194.         }
  195.         else {
  196.             IOTABLE.rangeInfo.range.base = REQUEST.userBufferStart;
  197.             IOTABLE.rangeInfo.range.length = REQUEST.amountToTransfer;
  198.             if (IOTABLE.rangeInfo.range.length > kNCRDriverMaxTransfer)
  199.                 IOTABLE.rangeInfo.range.length = kNCRDriverMaxTransfer;
  200.             REQUEST.amountToTransfer -= IOTABLE.rangeInfo.range.length;
  201.             (* (UInt32 *) &REQUEST.userBufferStart) +=
  202.                         (UInt32) IOTABLE.rangeInfo.range.length;
  203.             IOTABLE.mappingEntryCount = GetMapEntryCount(
  204.                         IOTABLE.rangeInfo.range.base,
  205.                         IOTABLE.rangeInfo.range.length
  206.                     );
  207.             if (IOTABLE.mappingEntryCount > REQUEST.scsiMapEntries)
  208.                 IOTABLE.mappingEntryCount = REQUEST.scsiMapEntries;
  209.             IOTABLE.physicalMapping = REQUEST.physicalMapTables;
  210.             IOTABLE.state = 0;                    /* Clear done bit                    */
  211.             IOTABLE.firstPrepared = 0;
  212.             status = PrepareMemoryForIO(&IOTABLE);
  213.             CheckStatus(status, "\pPrepareMemoryForIO initial");
  214.             if (status == noErr && IOTABLE.lengthPrepared == 0)
  215.                 status = scsiDataRunError;
  216.         }
  217.         if (status == noErr) {
  218.             REQUEST.dmaFirstPrepared = 0;
  219.             REQUEST.dmaLengthPrepared = 0;
  220.             status = PrepareNextDMA(perRequestDataPtr);
  221.         }
  222.         return (status);
  223. }
  224.  
  225. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  226.  * PrepareNextDMA handles the details of a single DMA burst. Here, we assume that we
  227.  * cannot build a physical scatter-gather table, but must construct each DMA transfer
  228.  * by itself. It uses the following from the REQUEST record:
  229.  *    REQUEST.scsiIOTable            This will be prepared by the initial call
  230.  *                                and used by other calls.
  231.  *    REQUEST.amountToTransfer    The number of bytes remaining in the user
  232.  *                                ioReqCount.
  233.  *    REQUEST.userBufferStart        The start of the user buffer -- updated when
  234.  *                                we call PrepareMemoryForIO for a new preparation.
  235.  *    REQUEST.userBufferEnd        The permanent end of the user buffer.
  236.  *    REQUEST.dmaFirstPrepared    Offset to already prepared data. This is always
  237.  *                                >= perRequestIOTable.firstPrepared.
  238.  *    REQUEST.dmaLengthPrepared    Amount already prepared. This is always
  239.  *                                <= perRequestIOTable.lengthPrepared.
  240.  *    REQUEST.physicalMapIndex    Where to start the next I/O (output only).
  241.  * Return:
  242.  *    noErr                        Success -- start or continue the transfer.
  243.  *    scsiDataRunError            Failure - no more data can be transfered.
  244.  *    other errors                Failure.
  245.  * Return scsiDataRunError if we're at the end of the transfer, or noErr if we
  246.  * did the preparation. The Primary Interrupt routine will send a Software Interrupt,
  247.  * other states will fail.
  248.  */
  249. OSErr
  250. PrepareNextDMA(
  251.         PerRequestDataPtr        perRequestDataPtr
  252.     )
  253. {
  254.         OSErr                    status;
  255.         ItemCount                index;
  256.  
  257.         Trace(PrepareNextDMA);
  258.         if (REQUEST.dmaFirstPrepared >= IOTABLE.lengthPrepared) {
  259.             /*
  260.              * We are off the end of the preparation. The caller must check the
  261.              * caller's ioReqCount to see if this terminates I/O. If not, the caller
  262.              * will, eventually, use a Software Interrupt to call PrepareMemoryForIO.
  263.              * When that happens, IOTABLE.firstPrepared will have been advanced to
  264.              * continue the preparation.
  265.              */
  266.             IOTABLE.firstPrepared += IOTABLE.lengthPrepared;
  267.             REQUEST.dmaLengthPrepared = 0;
  268.             status = scsiDataRunError;
  269.             //** LogString("\pOff the end of the preparation");
  270.         }
  271.         else {
  272.             status = noErr;
  273.             REQUEST.dmaLengthPrepared = GLOBAL.pageSize;
  274.             if (REQUEST.dmaFirstPrepared < GLOBAL.pageSize) {
  275.                 /*
  276.                  * This is the first preparation. It starts at the first physical
  277.                  * page entry (which is not necessarily page aligned) and extends
  278.                  * -- at most -- to the end of the page.
  279.                  */
  280.                 REQUEST.physicalMapIndex = 0;
  281.                 REQUEST.dmaLengthPrepared -=
  282.                     ((UInt32) IOTABLE.physicalMapping[0] & GLOBAL.pageMask);
  283.             }
  284.             else {
  285.                 /*
  286.                  * We are in the middle of a DMAsequence. The transfer length is a
  287.                  * page size and we start at the current preparation.
  288.                  */
  289.                 REQUEST.physicalMapIndex =
  290.                     ((REQUEST.dmaFirstPrepared - 1) / GLOBAL.pageSize) + 1;
  291.             }
  292.             /*
  293.              * We know where to start the transfer. Compute the number of bytes to
  294.              * transfer on this DMA cycle, extending the transfer as long as there
  295.              * are contiguous physical pages.
  296.              */
  297.             index = REQUEST.physicalMapIndex;
  298.             while (REQUEST.dmaLengthPrepared < IOTABLE.lengthPrepared
  299.                     && NextPageIsContiguous(&IOTABLE, index)) {
  300.                 REQUEST.dmaLengthPrepared += GLOBAL.pageSize;
  301.                 index++;
  302.             }
  303.             if (REQUEST.dmaLengthPrepared > IOTABLE.lengthPrepared)
  304.                 REQUEST.dmaLengthPrepared = IOTABLE.lengthPrepared;
  305.         }
  306. #if 0 && USE_LOG_LIBRARY
  307.         WriteLogEntry(GLOBAL.logRecordPtr, 'pDMA',
  308.             LogFormat5(kLogFormatSigned, kLogFormatUnsigned, kLogFormatUnsigned,
  309.                 kLogFormatAddress, kLogFormatString),
  310.             (signed long) status,
  311.             REQUEST.dmaFirstPrepared,
  312.             REQUEST.dmaLengthPrepared,
  313.             IOTABLE.physicalMapping[REQUEST.physicalMapIndex],
  314.             "\psts 1st len add"
  315.         );
  316. #endif
  317.         return (status);
  318. }
  319.